export class StringTool {
  /**
   * 格式化字符串，使用给定参数替换占位符。
   * @param template - 包含占位符的模板字符串。
   * @param args - 用于替换占位符的实际值。
   */
  format(template: string, ...args: any[]): string {
    return args.reduce(
      (acc, current, index) => acc.replace(`{${index}}`, String(current)),
      template
    )
  }

  /**
   * 动态解析字符串模版
   * let ss = x.str.template('/api/meta/table/{{page.object}}-{{biz}}-{{object}}', {
   *  page: { object: 123 },
   *  object: '参数2',
   *  biz: '参数3'
   * })
   * @param str 字符串模版
   * @param data 参数对象
   * @returns 解析后的字符串
   */
  template = (str: string, data: Record<string, any>): string => {
    return str.replace(/\{\{([\w.]+)\}\}/g, (match, p1) => {
      const value = getNestedValue(data, p1)
      return value !== null && value !== undefined ? String(value) : match
    })
  }

  /**
   * 截取字符串，多余的部分用...代替。
   * @param str - 待截取字符串。
   * @param maxLen - 字符串实际长度，中文2，英文1。
   */
  clip(str: string, maxLen: number): string {
    if (str === '') return ''

    let result = ''
    let length = 0

    for (let i = 0; i < str.length && length <= maxLen; i++) {
      const char = str.charAt(i)
      length += char.charCodeAt(0) > 128 ? 2 : 1
      if (length <= maxLen) {
        result += char
      }
    }

    return length > maxLen ? result + '...' : result
  }

  /**
   * 判断是否是数字。
   * @param value - 需要判断的值。
   */
  isNum(value: any): boolean {
    return value != null && !isNaN(Number(value)) && value.trim().length > 0
  }

  /**
   * 判断是否包含中文字符。
   * @param str - 需要判断的字符串。
   */
  isCn(str: string): boolean {
    const reg = /[\u4E00-\u9FA5\uFE30-\uFFA0]/
    return reg.test(str)
  }

  /**
   * URL编码。
   * @param url - 需要编码的URL。
   */
  enurl(url: string): string {
    return encodeURIComponent(url)
  }

  /**
   * URL解码。
   * @param url - 需要解码的URL。
   */
  deurl(url: string): string {
    return decodeURIComponent(url)
  }
}

/**
 * 获取嵌套对象值
 * @param data
 * @param path
 * @returns
 */
function getNestedValue(data: Record<string, any>, path: string): any {
  return path
    .split('.')
    .reduce((obj, key) => (obj && obj[key] !== undefined ? obj[key] : null), data)
}
